// Burner Input Set dialog module
#include "burner.h"

HWND hInpsDlg = NULL;					// Handle to the Input Set Dialog
static HBRUSH whiteBGBrush;

unsigned int inpsInput = 0;				// The input number we are redefining
static struct BurnInputInfo bii;		// Info about the input
static int dlgState = 0;
static int inputCode = -1;				// If in state 3, code N was inputCode
static int counter = 0;					// Counter of frames since the dialog was made
static GameInp* pgi = NULL;				// Current GameInp
static GameInp oldInp;					// Old GameInp
static int oldPush = 0;					// 1 if the push button was pressed last time
static bool grabMouse = false;
static bool oldLeftAltkeyMapped;

static int inpsInit()
{
	wchar_t szTitle[MAX_PATH];
	memset(&oldInp, 0, sizeof(oldInp));

	pgi = NULL;
	if (inpsInput >= gameInpCount + macroCount) {		// input out of range
		return 1;
	}
	pgi = gameInput + inpsInput;

	memset(&bii,0,sizeof(bii));
	BurnDrvGetInputInfo(&bii, inpsInput);

	if (bii.nType & BIT_GROUP_CONSTANT) {				// This dialog doesn't handle constants
		return 1;
	}

	oldInp = *pgi;
	oldPush = 0;

	grabMouse = false;

	oldLeftAltkeyMapped = leftAltkeyMapped;
	leftAltkeyMapped = true;

	// Set the dialog title
	if (inpsInput >= gameInpCount) {
		// Macro
		swprintf(szTitle, localiseStringEx(IDS_INPSET_MOVENAME), pgi->Macro.szName);
	} else {
		// Normal input
		if (bii.szName == NULL || bii.szName[0] == L'\0') {
			swprintf(szTitle, localiseStringEx(IDS_INPSET_MOVE));
		} else {
			swprintf(szTitle, localiseStringEx(IDS_INPSET_MOVENAME), bii.szName);
		}
	}
	SetWindowText(hInpsDlg, szTitle);

	inputInterface.find(2);

	dlgState = 4;
	inputCode = -1;
	counter = 0;

	return 0;
}

static int inpsExit()
{
	oldPush = 0;
	if (pgi != NULL) {
		*pgi = oldInp;
	}
	memset(&oldInp, 0, sizeof(oldInp));

	leftAltkeyMapped = oldLeftAltkeyMapped;

	dlgState = 0;

	return 0;
}

static int setInput(int code)
{
	if ((pgi->nInput & GIT_GROUP_MACRO) == 0) {

		if (bii.nType & BIT_GROUP_CONSTANT) {				// Don't change dip switches!
			DestroyWindow(hInpsDlg);
			dialogManager.remove(IDD_INPS);
			return 0;
		}

		if ((bii.nType & BIT_GROUP_ANALOG) && (code & 0xFF) < 0x10) {	// Analog controls
			if (strcmp(bii.szInfo + 4, "-axis-neg") == 0 || strcmp(bii.szInfo + 4, "-axis-pos") == 0) {
				if ((code & 0xF000) == 0x4000) {
					if (code & 1) {
						pgi->nInput = GIT_JOYAXIS_POS;
					} else {
						pgi->nInput = GIT_JOYAXIS_NEG;
					}
					pgi->Input.JoyAxis.nJoy = (code & 0x0F00) >> 8;
					pgi->Input.JoyAxis.nAxis = (code & 0x0F) >> 1;
				}
			} else {										// Map entire axis
				if ((code & 0xF000) == 0x4000) {
					pgi->nInput = GIT_JOYAXIS_FULL;
					pgi->Input.JoyAxis.nJoy = (code & 0x0F00) >> 8;
					pgi->Input.JoyAxis.nAxis = (code & 0x0F) >> 1;
				} else {
					pgi->nInput = GIT_MOUSEAXIS;
					pgi->Input.MouseAxis.nMouse = (code & 0x0F00) >> 8;
					pgi->Input.MouseAxis.nAxis = (code & 0x0F) >> 1;
				}
			}
		} else {
			pgi->nInput = GIT_SWITCH;
			pgi->Input.Switch.nCode = (unsigned short)code;
		}
	} else {
		pgi->Macro.nMode = 0x01;							// Mark macro as in use
		pgi->Macro.Switch.nCode = (unsigned short)code;		// Assign switch
	}

	oldInp = *pgi;

	inpdListMake(0);					// Update list with new input type

	return 0;
}

static int inpsPushUpdate()
{
	int pushState = 0;

	if (pgi == NULL || inpsInput >= gameInpCount) {
		return 0;
	}

	// See if the push button is pressed
	pushState = SendDlgItemMessage(hInpsDlg, IDC_INPS_PUSH, BM_GETSTATE, 0, 0);
	if (pushState & BST_PUSHED) {
		pushState = 1;
	} else {
		pushState = 0;
	}

	if (pushState) {
		switch (oldInp.nType) {
			case BIT_DIGITAL:			// Set digital inputs to 1
				pgi->nInput = GIT_CONSTANT;
				pgi->Input.Constant.nConst = 1;
				break;
			case BIT_DIPSWITCH:			// Set dipswitch block to 0xFF
				pgi->nInput = GIT_CONSTANT;
				pgi->Input.Constant.nConst = 0xFF;
				break;
		}
	} else {
		// Change back
		*pgi = oldInp;
	}
	if (pushState != oldPush) {		// refresh view
		inpdListMake(0);
	}

	oldPush = pushState;

	return pushState;
}

static void inpsUpdateControl(int code)
{
	wchar_t szString[MAX_PATH];
	wchar_t szDevice[MAX_PATH];
	wchar_t szControl[MAX_PATH];

	swprintf(szString, L"%s", inputCodeDesc(code));
	SetWindowText(GetDlgItem(hInpsDlg, IDC_INPS_CONTROL), szString);

	inputInterface.getControlName(code, szDevice, szControl);
	_snwprintf(szString, sizearray(szString), L"%s %s", szDevice, szControl);
	SetWindowText(GetDlgItem(hInpsDlg, IDC_INPS_CONTROL_NAME), szString);
}

int inpsUpdate()
{
	wchar_t strTemp[MAX_PATH];
	int buttonState;
	int find = -1;

	if (hInpsDlg == NULL) {				// Don't do anything if the dialog isn't created
		return 1;
	}
	if (counter < 0x100000) {			// advance frames since dialog was created
		counter++;
	}

	if (inpsPushUpdate()) {
		return 0;
	}

	buttonState = SendDlgItemMessage(hInpsDlg, IDC_INPS_GRABMOUSE, BM_GETSTATE, 0, 0);
	if (grabMouse) {
		if ((buttonState & BST_CHECKED) == 0) {
			grabMouse = false;
			dlgState = 2;
			return 0;
		}
	} else {
		if (buttonState & BST_CHECKED) {
			grabMouse = true;
			dlgState = 4;
			return 0;
		}
	}
	// This doesn't work properly
	if (buttonState & BST_PUSHED) {
		return 0;
	}

	buttonState = SendDlgItemMessage(hInpsDlg, IDCANCEL, BM_GETSTATE, 0, 0);
	if (buttonState & BST_PUSHED) {
		return 0;
	}

	find = inputInterface.find(dlgState);

	if (dlgState & 4) {					//  4 = Waiting for all controls to be released
		if (grabMouse ? (find >= 0) : (find >= 0 && find < 0x8000)) {
			if (counter >= 60) {
				// Alert the user that a control is stuck
				swprintf(strTemp, localiseStringEx(IDS_INPSET_WAITING), inputCodeDesc(find));
				SetWindowText(GetDlgItem(hInpsDlg, IDC_INPS_CONTROL), strTemp);

				counter = 0;
			}
			return 0;
		}

		// All keys released
		SetWindowText(GetDlgItem(hInpsDlg, IDC_INPS_CONTROL), L"");
		SetWindowText(GetDlgItem(hInpsDlg, IDC_INPS_CONTROL_NAME), L"");
		dlgState = 8;
	}

	if (dlgState & 8) {					//  8 = Waiting for a control to be activated
		if (grabMouse ? (find < 0) : (find < 0 || find >= 0x8000)) {
			return 0;
		}

		// Key pressed
		inputCode = find;
		inpsUpdateControl(inputCode);

		dlgState = 16;
	}

	if (dlgState & 16) {				// 16 = waiting for control to be released
		if (grabMouse ? find >= 0 : (find >= 0 && find < 0x8000)) {
			if (inputCode != find) {
				inputCode = find;
				inpsUpdateControl(inputCode);
			}
			return 0;
		}

		// Key released
		setInput(inputCode);

		dlgState = 0;
		DestroyWindow(hInpsDlg);		// Quit dialog
		dialogManager.remove(IDD_INPS);
	}

	return 0;
}

static INT_PTR CALLBACK inpsDialogProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
	if (msg == WM_INITDIALOG) {
		hInpsDlg = hDlg;
		whiteBGBrush = CreateSolidBrush(RGB(0xFF,0xFF,0xFF));
		if (inpsInit()) {
			DestroyWindow(hInpsDlg);
			dialogManager.remove(IDD_INPS);
			return FALSE;
		}
		SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, IDC_INPS_CONTROL), TRUE);
		return FALSE;
	}
	if (msg == WM_CLOSE) {
		DestroyWindow(hInpsDlg);
		dialogManager.remove(IDD_INPS);
		return 0;
	}
	if (msg == WM_DESTROY) {
		DeleteObject(whiteBGBrush);
		inpsExit();
		hInpsDlg = NULL;
		return 0;
	}
	if (msg == WM_COMMAND) {
		int Id = LOWORD(wParam);
		int Notify = HIWORD(wParam);
		if (Id == IDCANCEL && Notify == BN_CLICKED) {
			SendMessage(hDlg, WM_CLOSE, 0, 0);
			return 0;
		}
	}
	if (msg == WM_CTLCOLORSTATIC) {
		if ((HWND)lParam == GetDlgItem(hDlg, IDC_INPS_CONTROL)) {
			return (BOOL)whiteBGBrush;
		}
	}
	return 0;
}

int inpsCreate()
{
	dialogManager.remove(IDD_INPS);
	DestroyWindow(hInpsDlg); // Make sure exitted
	hInpsDlg = NULL;

	hInpsDlg = localiseCreateDialog(IDD_INPS, dialogManager.get(IDD_INPD), (DLGPROC)inpsDialogProc);
	if (hInpsDlg == NULL) {
		return 1;
	}
	dialogManager.add(IDD_INPS, hInpsDlg);

	wndInMid(hInpsDlg, dialogManager.get(IDD_INPD));
	ShowWindow(hInpsDlg, SW_NORMAL);

	return 0;
}
